package jam4j.lang;

import jam4j.util.FallibleThunk;
import jam4j.util.Strings;

import java.util.*;

public final class ActionType {
    private final Rule rule;
    private final Expression body;
    private final String[] boundTargets;
    private final Set<Modifier> modifiers;
    private final int maxLineLength;
    
    enum Modifier {
        /** Ignore source targets that don't currently exist. */
        EXISTING,
        /** Ignore the return status of the commands; always succeed. */
        IGNORE,
        /**
         * Split the source targets into subsets such that the command lines
         * will fit into this OS's command line buffer. 
         */
        PIECEMEAL,
        /** Don't echo the command to standard output. */
        QUIETLY,
        /** Perform all actions called on the same built target at once. */
        TOGETHER,
        /** Ignore source targets that aren't themselves marked for updating.*/
        UPDATED
    }
    
    public static final Modifier EXISTING   = Modifier.EXISTING;
    public static final Modifier IGNORE     = Modifier.IGNORE;
    public static final Modifier PIECEMEAL  = Modifier.PIECEMEAL;
    public static final Modifier QUIETLY    = Modifier.QUIETLY;
    public static final Modifier TOGETHER   = Modifier.TOGETHER;
    public static final Modifier UPDATED    = Modifier.UPDATED;
    
    ActionType(Rule rule, Expression body,
            String[] boundTargets, Modifier ... modifiers) {
        this(rule, body, -1, boundTargets, modifiers);
    }
    
    ActionType(Rule rule, Expression body, int maxLineLength,
            String[] boundTargets, Modifier ... modifiers) {
        this.rule = rule;
        this.body = body;
        this.maxLineLength = maxLineLength;
        this.boundTargets = boundTargets;
        this.modifiers = EnumSet.noneOf(Modifier.class);
        Collections.addAll(this.modifiers, modifiers);
    }
    
    ActionType(Rule rule, Expression body,
            List<String> boundTargets, Collection<Modifier> modifiers) {
        this(rule, body, -1, boundTargets, modifiers);
    }
    
    ActionType(Rule rule, Expression body, int maxLineLength,
            List<String> boundTargets, Collection<Modifier> modifiers) {
        this(rule, body, maxLineLength,
                boundTargets.toArray(new String[boundTargets.size()]),
                modifiers.toArray(new Modifier[modifiers.size()]));
    }
    
    public boolean hasModifier(Modifier modifier) {
        return modifiers.contains(modifier);
    }
    
    public int maxLineLength() {
        return maxLineLength;
    }

    public <T, X extends Throwable> T onBoundTargets(
            Context cxt, FallibleThunk<T, X> thunk) throws X {
        cxt.enterOnBoundTargets(boundTargets);
        try {
            return thunk.call();
        } finally {
            cxt.leaveScope();
        }
    }

    public String command(Context cxt, String[][] args) {
        final Scope scope = cxt.enterScope();
        try {
            scope.set("1", args[0]);
            scope.set("2", args[1]);
            return body.resolveCommand(cxt);
        } finally {
            cxt.leaveScope();
        }
    }
    
    public String name() {
        return rule.name();
    }
    
    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder("ActionType ");
        return Strings.prettyQuote(rule.name(), builder).toString();
    }
}
